package com.boyon.web.servlet.tomcat;

import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;

import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.SessionListener;
import org.apache.catalina.security.SecurityUtil;
import org.apache.tomcat.util.res.StringManager;

import com.boyon.common.tools.ObjectTool;
import com.boyon.redis.wrap.JedisWrap;

import lombok.extern.slf4j.Slf4j;

/**
 * ClassName:TomcatSession <br/>
 * Function: tomcat StandardSession扩展类. <br/>
 * Date: 2018年3月26日 下午9:48:50 <br/>
 * 
 * @author sunshulin
 * @version
 * @since JDK 1.8
 * @see
 */
@SuppressWarnings("deprecation")
@Slf4j
public class TomcatSession implements HttpSession, Session, Serializable {

	/**
	 * serialVersionUID:.
	 * 
	 * @since JDK 1.8
	 */
	private static final long serialVersionUID = -2182252101507274184L;

	protected transient Manager manager = null;
	protected transient TomcatSessionFace facade = null;
	protected volatile boolean isNew = false;
	protected transient String authType = null;
	protected final transient PropertyChangeSupport support = new PropertyChangeSupport(
			this);
	protected volatile boolean isValid = false;

	/**
	 * session访问时间
	 */
	protected long creationTime = 0L;
	protected volatile long lastAccessedTime = creationTime;
	protected volatile long thisAccessedTime = creationTime;
	protected String id = null;
	protected volatile int maxInactiveInterval = -1;
	protected transient volatile boolean expiring = false;
	protected transient Principal principal = null;
	protected transient ArrayList<SessionListener> listeners = new ArrayList<>();
	protected transient AtomicInteger accessCount = null;
	protected transient Map<String, Object> notes = new Hashtable<>();

	protected ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<>();
	protected static final StringManager sm = StringManager
			.getManager(TomcatSession.class);

	/**
	 * Creates a new instance of TomcatSession.
	 * 
	 * @param manager
	 */
	public TomcatSession(Manager manager) {
		super();
		this.manager = manager;
		log.debug("构建tomcat session {}", manager);
	}

	/**
	 * 
	 * 获取session.
	 * 
	 * @see org.apache.catalina.Session#getSession()
	 */
	public HttpSession getSession() {
		if (facade == null) {
			if (SecurityUtil.isPackageProtectionEnabled()) {
				final TomcatSession tsession = this;
				facade = AccessController.doPrivileged(
						new PrivilegedAction<TomcatSessionFace>() {
							public TomcatSessionFace run() {
								return new TomcatSessionFace(tsession);
							}
						});
			} else {
				facade = new TomcatSessionFace(this);
			}
		}
		return (facade);
	}

	/**
	 * 
	 * 设置保存session属性.
	 * 
	 * @see org.apache.catalina.session.StandardSession#setAttribute(java.lang.String,
	 *      java.lang.Object, boolean)
	 */
	public void setAttribute(String name, Object value) {
		TomcatManager tomcat = (TomcatManager) manager;
		JedisWrap jedisWrap = tomcat.getJedisWrap();
		try {
			jedisWrap.selectDatabase(TomcatManager.database);
			byte[] sessionId = getId().getBytes();
			jedisWrap.hset(sessionId, name.getBytes(),
					ObjectTool.toByteArray(value));
		} finally {
			jedisWrap.release();
		}
	}

	/**
	 * 获取session属性值
	 * 
	 * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String)
	 */
	public Object getAttribute(String name) {
		TomcatManager tomcat = (TomcatManager) manager;
		JedisWrap jedisWrap = tomcat.getJedisWrap();
		try {
			jedisWrap.selectDatabase(TomcatManager.database);
			byte[] sessionId = getId().getBytes();
			byte[] bytes = jedisWrap.hget(sessionId, name.getBytes());
			if (ObjectTool.isNotNull(bytes)) {
				return ObjectTool.toObject(bytes);
			}
		} finally {
			jedisWrap.release();
		}
		return null;
	}

	/**
	 * 删除session属性
	 * 
	 * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String)
	 */
	public void removeAttribute(String name) {
		TomcatManager tomcat = (TomcatManager) manager;
		JedisWrap jedisWrap = tomcat.getJedisWrap();
		try {
			jedisWrap.selectDatabase(TomcatManager.database);
			byte[] sessionId = getId().getBytes();
			jedisWrap.hdel(sessionId, name.getBytes());
		} finally {
			jedisWrap.release();
		}
	}

	/**
	 * 
	 * session访问
	 * 
	 * @see org.apache.catalina.Session#access()
	 */
	public void access() {
		TomcatManager tomcat = (TomcatManager) manager;
		JedisWrap jedisWrap = tomcat.getJedisWrap();
		try {
			jedisWrap.selectDatabase(TomcatManager.database);
			byte[] sessionId = getId().getBytes();
			jedisWrap.expire(sessionId, 3600);
		} finally {
			jedisWrap.release();
		}
	}

	/**
	 * 验证是否失效
	 * 
	 * @see org.apache.catalina.Session#isValid()
	 */
	public boolean isValid() {
		TomcatManager tomcat = (TomcatManager) manager;
		JedisWrap jedisWrap = tomcat.getJedisWrap();
		try {
			jedisWrap.selectDatabase(TomcatManager.database);
			byte[] sessionId = getId().getBytes();
			Boolean exist = jedisWrap.exists(sessionId);
			return ObjectTool.isNotNull(exist) && exist;
		} finally {
			jedisWrap.release();
		}
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getAuthType()
	 */
	public String getAuthType() {
		return (this.authType);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setAuthType(java.lang.String)
	 */
	public void setAuthType(String authType) {
		String oldAuthType = authType;
		this.authType = authType;
		support.firePropertyChange("authType", oldAuthType, this.authType);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getCreationTimeInternal()
	 */
	public long getCreationTimeInternal() {
		return this.creationTime;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setCreationTime(long)
	 */
	public void setCreationTime(long time) {
		this.creationTime = time;
		this.lastAccessedTime = time;
		this.thisAccessedTime = time;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getIdInternal()
	 */
	public String getIdInternal() {
		return this.id;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setId(java.lang.String)
	 */
	public void setId(String id) {
		this.id = id;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setId(java.lang.String, boolean)
	 */
	public void setId(String id, boolean notify) {

	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getThisAccessedTime()
	 */
	public long getThisAccessedTime() {
		return this.thisAccessedTime;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getThisAccessedTimeInternal()
	 */
	public long getThisAccessedTimeInternal() {
		return this.thisAccessedTime;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getLastAccessedTimeInternal()
	 */
	public long getLastAccessedTimeInternal() {
		return this.lastAccessedTime;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getIdleTime()
	 */
	public long getIdleTime() {
		return 0;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getIdleTimeInternal()
	 */
	public long getIdleTimeInternal() {
		long timeNow = System.currentTimeMillis();
		long timeIdle;
		timeIdle = timeNow - lastAccessedTime;
		return timeIdle;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getManager()
	 */
	public Manager getManager() {
		return this.manager;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setManager(org.apache.catalina.Manager)
	 */
	public void setManager(Manager manager) {
		this.manager = manager;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setNew(boolean)
	 */
	public void setNew(boolean isNew) {
		this.isNew = isNew;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getPrincipal()
	 */
	public Principal getPrincipal() {
		return this.principal;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setPrincipal(java.security.Principal)
	 */
	public void setPrincipal(Principal principal) {
		Principal oldPrincipal = this.principal;
		this.principal = principal;
		support.firePropertyChange("principal", oldPrincipal, this.principal);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setValid(boolean)
	 */
	public void setValid(boolean isValid) {
		this.isValid = isValid;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#addSessionListener(org.apache.catalina.SessionListener)
	 */
	public void addSessionListener(SessionListener listener) {
		listeners.add(listener);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#endAccess()
	 */
	public void endAccess() {
		isNew = false;
		this.lastAccessedTime = this.thisAccessedTime;
		this.thisAccessedTime = System.currentTimeMillis();
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#expire()
	 */
	public void expire() {

	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getNote(java.lang.String)
	 */
	public Object getNote(String name) {
		return notes.get(name);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#getNoteNames()
	 */
	public Iterator<String> getNoteNames() {
		return notes.keySet().iterator();
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#recycle()
	 */
	public void recycle() {
		attributes.clear();
		setAuthType(null);
		creationTime = 0L;
		expiring = false;
		id = null;
		lastAccessedTime = 0L;
		maxInactiveInterval = -1;
		notes.clear();
		setPrincipal(null);
		isNew = false;
		isValid = false;
		manager = null;
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#removeNote(java.lang.String)
	 */
	public void removeNote(String name) {
		notes.remove(name);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#removeSessionListener(org.apache.catalina.SessionListener)
	 */
	public void removeSessionListener(SessionListener listener) {
		listeners.remove(listener);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#setNote(java.lang.String,
	 *      java.lang.Object)
	 */
	public void setNote(String name, Object value) {
		notes.put(name, value);
	}

	/**
	 * 
	 * @see org.apache.catalina.Session#tellChangedSessionId(java.lang.String,
	 *      java.lang.String, boolean, boolean)
	 */
	public void tellChangedSessionId(String newId, String oldId,
			boolean notifySessionListeners, boolean notifyContainerListeners) {

	}

	/**
	 * 
	 * @see org.apache.catalina.Session#isAttributeDistributable(java.lang.String,
	 *      java.lang.Object)
	 */
	public boolean isAttributeDistributable(String name, Object value) {
		return value instanceof Serializable;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getCreationTime()
	 */
	public long getCreationTime() {
		return this.creationTime;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getId()
	 */
	public String getId() {
		return this.id;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getLastAccessedTime()
	 */
	public long getLastAccessedTime() {
		if (!isValidInternal()) {
			throw new IllegalStateException(
					sm.getString("standardSession.getLastAccessedTime.ise"));
		}
		return this.lastAccessedTime;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getServletContext()
	 */
	public ServletContext getServletContext() {
		return null;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int)
	 */
	public void setMaxInactiveInterval(int interval) {

	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
	 */
	public int getMaxInactiveInterval() {
		return 0;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getSessionContext()
	 */
	public HttpSessionContext getSessionContext() {
		return null;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getValue(java.lang.String)
	 */
	public Object getValue(String name) {
		return null;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getAttributeNames()
	 */
	public Enumeration<String> getAttributeNames() {
		return null;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#getValueNames()
	 */
	public String[] getValueNames() {
		return null;
	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#putValue(java.lang.String,
	 *      java.lang.Object)
	 */
	@Override
	public void putValue(String name, Object value) {

	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#invalidate()
	 */
	public void invalidate() {

	}

	/**
	 * 
	 * @see javax.servlet.http.HttpSession#isNew()
	 */
	public boolean isNew() {
		return this.isNew;
	}

	protected boolean isValidInternal() {
		return this.isValid;
	}

	/**
	 * @see javax.servlet.http.HttpSession#removeValue(java.lang.String)
	 */
	public void removeValue(String name) {

	}
}
